/******************************************************************************
 * Copyright (C) 2023 Maxim Integrated Products, Inc., All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES
 * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 *
 * Except as contained in this notice, the name of Maxim Integrated
 * Products, Inc. shall not be used except as stated in the Maxim Integrated
 * Products, Inc. Branding Policy.
 *
 * The mere transfer of this software does not imply any licenses
 * of trade secrets, proprietary technology, copyrights, patents,
 * trademarks, maskwork rights, or any other form of intellectual
 * property whatsoever. Maxim Integrated Products, Inc. retains all
 * ownership rights.
 *
 ******************************************************************************/

#include "uart.h"
#include "mxc_device.h"
#include "mxc_pins.h"
#include "mxc_assert.h"
#include "uart_revb.h"
#include "uart_common.h"
#include "mcr_regs.h"
#include "dma.h"

void MXC_UART_DMACallback(int ch, int error)
{
    MXC_UART_RevB_DMACallback(ch, error);
}

int MXC_UART_AsyncCallback(mxc_uart_regs_t *uart, int retVal)
{
    return MXC_UART_RevB_AsyncCallback((mxc_uart_revb_regs_t *)uart, retVal);
}

int MXC_UART_AsyncStop(mxc_uart_regs_t *uart)
{
    return MXC_UART_RevB_AsyncStop((mxc_uart_revb_regs_t *)uart);
}

int MXC_UART_Init(mxc_uart_regs_t *uart, unsigned int baud, mxc_uart_clock_t clock, sys_map_t map)
{
    int retval;

    retval = MXC_UART_Shutdown(uart);

    if (retval) {
        return retval;
    }

    switch (clock) {
    case MXC_UART_EXT_CLK:
        if (uart == MXC_UART3) {
            MXC_GPIO_Config(&gpio_cfg_lpextclk);
        } else {
            MXC_GPIO_Config(&gpio_cfg_hfextclk);
        }
        break;

    case MXC_UART_ERTCO_CLK:
        MXC_SYS_ClockSourceEnable(MXC_SYS_CLOCK_ERTCO);
        break;

    case MXC_UART_IBRO_CLK:
        MXC_SYS_ClockSourceEnable(MXC_SYS_CLOCK_IBRO);
        break;

    case MXC_UART_ERFO_CLK:
        MXC_SYS_ClockSourceEnable(MXC_SYS_CLOCK_ERFO);
        break;

    case MXC_UART_INRO_CLK:
        MXC_SYS_ClockSourceEnable(MXC_SYS_CLOCK_INRO);
        break;

    default:
        break;
    }

    switch (MXC_UART_GET_IDX(uart)) {
    case 0:
        if (map == MAP_B) {
            MXC_GPIO_Config(&gpio_cfg_uart0b);
        } else {
            MXC_GPIO_Config(&gpio_cfg_uart0a);
        }
        MXC_SYS_ClockEnable(MXC_SYS_PERIPH_CLOCK_UART0);
        break;

    case 1:
        if (map == MAP_B) {
            MXC_GPIO_Config(&gpio_cfg_uart1b);
        } else {
            MXC_GPIO_Config(&gpio_cfg_uart1a);
        }
        MXC_SYS_ClockEnable(MXC_SYS_PERIPH_CLOCK_UART1);
        break;

    case 2:
        MXC_GPIO_Config(&gpio_cfg_uart2b);
        MXC_SYS_ClockEnable(MXC_SYS_PERIPH_CLOCK_UART2);
        break;

    case 3:
        MXC_GPIO_Config(&gpio_cfg_uart3);
        MXC_SYS_ClockEnable(MXC_SYS_PERIPH_CLOCK_UART3);
        MXC_MCR->lppioctrl |= MXC_F_MCR_LPPIOCTRL_LPUART0_RX | MXC_F_MCR_LPPIOCTRL_LPUART0_TX |
                              MXC_F_MCR_LPPIOCTRL_LPUART0_RTS | MXC_F_MCR_LPPIOCTRL_LPUART0_CTS;
        break;

    default:
        return E_NOT_SUPPORTED;
    }

    return MXC_UART_RevB_Init((mxc_uart_revb_regs_t *)uart, baud, clock);
}

int MXC_UART_Shutdown(mxc_uart_regs_t *uart)
{
    switch (MXC_UART_GET_IDX(uart)) {
    case 0:
        MXC_SYS_Reset_Periph(MXC_SYS_RESET0_UART0);
        MXC_SYS_ClockDisable(MXC_SYS_PERIPH_CLOCK_UART0);
        break;

    case 1:
        MXC_SYS_Reset_Periph(MXC_SYS_RESET0_UART1);
        MXC_SYS_ClockDisable(MXC_SYS_PERIPH_CLOCK_UART1);
        break;

    case 2:
        MXC_SYS_Reset_Periph(MXC_SYS_RESET0_UART2);
        MXC_SYS_ClockDisable(MXC_SYS_PERIPH_CLOCK_UART2);
        break;

    case 3:
        MXC_SYS_Reset_Periph(MXC_SYS_RESET_UART3);
        MXC_SYS_ClockDisable(MXC_SYS_PERIPH_CLOCK_UART3);
        MXC_MCR->lppioctrl |= ~(MXC_F_MCR_LPPIOCTRL_LPUART0_RX | MXC_F_MCR_LPPIOCTRL_LPUART0_TX |
                                MXC_F_MCR_LPPIOCTRL_LPUART0_RTS | MXC_F_MCR_LPPIOCTRL_LPUART0_CTS);
        break;

    default:
        return E_NOT_SUPPORTED;
    }

    return E_NO_ERROR;
}

int MXC_UART_ReadyForSleep(mxc_uart_regs_t *uart)
{
    return MXC_UART_RevB_ReadyForSleep((mxc_uart_revb_regs_t *)uart);
}

int MXC_UART_SetFrequency(mxc_uart_regs_t *uart, unsigned int baud, mxc_uart_clock_t clock)
{
    int freq;
    int mod = 0;
    int clkdiv = 0;
    int div = 8;

    if (MXC_UART_GET_IDX(uart) < 0) {
        return E_BAD_PARAM;
    }

    // check if the uart is LPUART
    if (uart == MXC_UART3) {
        // OSR default value
        uart->osr = 5;

        switch (clock) {
        case MXC_UART_APB_CLK:
            uart->ctrl |= MXC_S_UART_CTRL_BCLKSRC_PERIPHERAL_CLOCK;
            div = (1 << (MXC_GCR->pclkdiv & MXC_F_GCR_PCLKDIV_AON_CLKDIV)) * 8;
            clkdiv = ((SystemCoreClock / div) / baud);
            mod = ((SystemCoreClock / div) % baud);
            break;

        case MXC_UART_EXT_CLK:
            uart->ctrl |= MXC_S_UART_CTRL_BCLKSRC_EXTERNAL_CLOCK;
            clkdiv = EXTCLK_FREQ / baud;
            mod = EXTCLK_FREQ % baud;
            break;

        case MXC_UART_ERTCO_CLK:
            uart->ctrl |= MXC_S_UART_CTRL_BCLKSRC_CLK2;
            uart->ctrl |= MXC_F_UART_CTRL_FDM;
            clkdiv = ((ERTCO_FREQ * 2) / baud);
            mod = ((ERTCO_FREQ * 2) % baud);

            if (baud > 2400) {
                uart->osr = 0;
            } else {
                uart->osr = 1;
            }
            break;

        case MXC_UART_INRO_CLK:
            uart->ctrl |= MXC_S_UART_CTRL_BCLKSRC_CLK3;
            uart->ctrl |= MXC_F_UART_CTRL_FDM;
            clkdiv = ((INRO_FREQ * 2) / baud);
            mod = ((INRO_FREQ * 2) % baud);

            if (baud > 2400) {
                uart->osr = 0;
            } else {
                uart->osr = 1;
            }
            break;

        default:
            return E_BAD_PARAM;
        }

        if (!clkdiv || mod > (baud / 2)) {
            clkdiv++;
        }
        uart->clkdiv = clkdiv;

        freq = MXC_UART_GetFrequency(uart);
    } else {
        freq = MXC_UART_RevB_SetFrequency((mxc_uart_revb_regs_t *)uart, baud, clock);
    }

    if (freq > 0) {
        // Enable baud clock and wait for it to become ready.
        uart->ctrl |= MXC_F_UART_CTRL_BCLKEN;
        while (((uart->ctrl & MXC_F_UART_CTRL_BCLKRDY) >> MXC_F_UART_CTRL_BCLKRDY_POS) == 0) {}
    }

    return freq;
}

int MXC_UART_GetFrequency(mxc_uart_regs_t *uart)
{
    int periphClock = 0;
    int div = 8;

    if (MXC_UART_GET_IDX(uart) < 0) {
        return E_BAD_PARAM;
    }

    // check if UARt is LP UART
    if (uart == MXC_UART3) {
        if ((uart->ctrl & MXC_F_UART_CTRL_BCLKSRC) == MXC_S_UART_CTRL_BCLKSRC_EXTERNAL_CLOCK) {
            return EXTCLK_FREQ;
        } else if ((uart->ctrl & MXC_F_UART_CTRL_BCLKSRC) ==
                   MXC_S_UART_CTRL_BCLKSRC_PERIPHERAL_CLOCK) {
            div = (1 << (MXC_GCR->pclkdiv & MXC_F_GCR_PCLKDIV_AON_CLKDIV)) * 8;
            periphClock = SystemCoreClock / div;
        } else if ((uart->ctrl & MXC_F_UART_CTRL_BCLKSRC) == MXC_S_UART_CTRL_BCLKSRC_CLK2) {
            periphClock = ERTCO_FREQ * 2;
        } else if ((uart->ctrl & MXC_F_UART_CTRL_BCLKSRC) == MXC_S_UART_CTRL_BCLKSRC_CLK3) {
            periphClock = INRO_FREQ * 2;
        } else {
            return E_BAD_PARAM;
        }
        return (periphClock / uart->clkdiv);
    } else {
        return MXC_UART_RevB_GetFrequency((mxc_uart_revb_regs_t *)uart);
    }
}

int MXC_UART_SetDataSize(mxc_uart_regs_t *uart, int dataSize)
{
    return MXC_UART_RevB_SetDataSize((mxc_uart_revb_regs_t *)uart, dataSize);
}

int MXC_UART_SetStopBits(mxc_uart_regs_t *uart, mxc_uart_stop_t stopBits)
{
    return MXC_UART_RevB_SetStopBits((mxc_uart_revb_regs_t *)uart, stopBits);
}

int MXC_UART_SetParity(mxc_uart_regs_t *uart, mxc_uart_parity_t parity)
{
    return MXC_UART_RevB_SetParity((mxc_uart_revb_regs_t *)uart, parity);
}

int MXC_UART_SetFlowCtrl(mxc_uart_regs_t *uart, mxc_uart_flow_t flowCtrl, int rtsThreshold, sys_map_t map)
{
    if (flowCtrl == MXC_UART_FLOW_EN) {
        switch (MXC_UART_GET_IDX(uart)) {
        case 0:
            if (map == MAP_B) {
                MXC_GPIO_Config(&gpio_cfg_uart0b_flow);
            } else {
                MXC_GPIO_Config(&gpio_cfg_uart0a_flow);
            }
            break;

        case 1:
            if (map == MAP_B) {
                MXC_GPIO_Config(&gpio_cfg_uart1b_flow);
            } else {
                MXC_GPIO_Config(&gpio_cfg_uart1a_flow);
            }
            break;

        case 2:
            MXC_GPIO_Config(&gpio_cfg_uart2b_flow);
            break;

        case 3:
            MXC_GPIO_Config(&gpio_cfg_uart3_flow);
            break;

        default:
            return E_BAD_PARAM;
        }
    } else {
        switch (MXC_UART_GET_IDX(uart)) {
        case 0:
            if (map == MAP_B) {
                MXC_GPIO_Config(&gpio_cfg_uart0b_flow_disable);
            } else {
                MXC_GPIO_Config(&gpio_cfg_uart0a_flow_disable);
            }
            break;

        case 1:
            if (map == MAP_B) {
                MXC_GPIO_Config(&gpio_cfg_uart1b_flow_disable);
            } else {
                MXC_GPIO_Config(&gpio_cfg_uart1a_flow_disable);
            }
            break;

        case 2:
            MXC_GPIO_Config(&gpio_cfg_uart2b_flow_disable);
            break;

        case 3:
            MXC_GPIO_Config(&gpio_cfg_uart3_flow_disable);
            break;

        default:
            return E_BAD_PARAM;
        }
    }

    return MXC_UART_RevB_SetFlowCtrl((mxc_uart_revb_regs_t *)uart, flowCtrl, rtsThreshold);
}

int MXC_UART_SetClockSource(mxc_uart_regs_t *uart, mxc_uart_clock_t clock)
{
    return MXC_UART_RevB_SetClockSource((mxc_uart_revb_regs_t *)uart, clock);
}

int MXC_UART_GetActive(mxc_uart_regs_t *uart)
{
    return MXC_UART_RevB_GetActive((mxc_uart_revb_regs_t *)uart);
}

int MXC_UART_AbortTransmission(mxc_uart_regs_t *uart)
{
    return MXC_UART_RevB_AbortTransmission((mxc_uart_revb_regs_t *)uart);
}

int MXC_UART_ReadCharacterRaw(mxc_uart_regs_t *uart)
{
    return MXC_UART_RevB_ReadCharacterRaw((mxc_uart_revb_regs_t *)uart);
}

int MXC_UART_WriteCharacterRaw(mxc_uart_regs_t *uart, uint8_t character)
{
    return MXC_UART_RevB_WriteCharacterRaw((mxc_uart_revb_regs_t *)uart, character);
}

int MXC_UART_ReadCharacter(mxc_uart_regs_t *uart)
{
    return MXC_UART_Common_ReadCharacter(uart);
}

int MXC_UART_WriteCharacter(mxc_uart_regs_t *uart, uint8_t character)
{
    return MXC_UART_Common_WriteCharacter(uart, character);
}

int MXC_UART_Read(mxc_uart_regs_t *uart, uint8_t *buffer, int *len)
{
    return MXC_UART_RevB_Read((mxc_uart_revb_regs_t *)uart, buffer, len);
}

int MXC_UART_Write(mxc_uart_regs_t *uart, const uint8_t *byte, int *len)
{
    return MXC_UART_RevB_Write((mxc_uart_revb_regs_t *)uart, byte, len);
}

unsigned int MXC_UART_ReadRXFIFO(mxc_uart_regs_t *uart, unsigned char *bytes, unsigned int len)
{
    return MXC_UART_RevB_ReadRXFIFO((mxc_uart_revb_regs_t *)uart, bytes, len);
}

int MXC_UART_ReadRXFIFODMA(mxc_uart_regs_t *uart, unsigned char *bytes, unsigned int len,
                           mxc_uart_dma_complete_cb_t callback)
{
    mxc_dma_config_t config;

    int uart_num = MXC_UART_GET_IDX(uart);

    switch (uart_num) {
    case 0:
        config.reqsel = MXC_DMA_REQUEST_UART0RX;
        break;

    case 1:
        config.reqsel = MXC_DMA_REQUEST_UART1RX;
        break;

    case 2:
        config.reqsel = MXC_DMA_REQUEST_UART2RX;
        break;

    default:
        return E_BAD_PARAM;
        break;
    }

    return MXC_UART_RevB_ReadRXFIFODMA((mxc_uart_revb_regs_t *)uart, bytes, len, callback, config);
}

unsigned int MXC_UART_GetRXFIFOAvailable(mxc_uart_regs_t *uart)
{
    return MXC_UART_RevB_GetRXFIFOAvailable((mxc_uart_revb_regs_t *)uart);
}

unsigned int MXC_UART_WriteTXFIFO(mxc_uart_regs_t *uart, const unsigned char *bytes,
                                  unsigned int len)
{
    return MXC_UART_RevB_WriteTXFIFO((mxc_uart_revb_regs_t *)uart, bytes, len);
}

int MXC_UART_WriteTXFIFODMA(mxc_uart_regs_t *uart, const unsigned char *bytes, unsigned int len,
                            mxc_uart_dma_complete_cb_t callback)
{
    mxc_dma_config_t config;

    int uart_num = MXC_UART_GET_IDX(uart);

    switch (uart_num) {
    case 0:
        config.reqsel = MXC_DMA_REQUEST_UART0TX;
        break;

    case 1:
        config.reqsel = MXC_DMA_REQUEST_UART1TX;
        break;

    case 2:
        config.reqsel = MXC_DMA_REQUEST_UART2TX;
        break;

    default:
        return E_BAD_PARAM;
        break;
    }

    return MXC_UART_RevB_WriteTXFIFODMA((mxc_uart_revb_regs_t *)uart, bytes, len, callback, config);
}

unsigned int MXC_UART_GetTXFIFOAvailable(mxc_uart_regs_t *uart)
{
    return MXC_UART_RevB_GetTXFIFOAvailable((mxc_uart_revb_regs_t *)uart);
}

int MXC_UART_ClearRXFIFO(mxc_uart_regs_t *uart)
{
    return MXC_UART_RevB_ClearRXFIFO((mxc_uart_revb_regs_t *)uart);
}

int MXC_UART_ClearTXFIFO(mxc_uart_regs_t *uart)
{
    return MXC_UART_RevB_ClearTXFIFO((mxc_uart_revb_regs_t *)uart);
}

int MXC_UART_SetRXThreshold(mxc_uart_regs_t *uart, unsigned int numBytes)
{
    return MXC_UART_RevB_SetRXThreshold((mxc_uart_revb_regs_t *)uart, numBytes);
}

unsigned int MXC_UART_GetRXThreshold(mxc_uart_regs_t *uart)
{
    return MXC_UART_RevB_GetRXThreshold((mxc_uart_revb_regs_t *)uart);
}

int MXC_UART_SetTXThreshold(mxc_uart_regs_t *uart, unsigned int numBytes)
{
    // TX threshold is fixed at half the length of FIFO
    return E_NOT_SUPPORTED;
}

unsigned int MXC_UART_GetTXThreshold(mxc_uart_regs_t *uart)
{
    // TX threshold is fixed at half the length of FIFO
    return E_NOT_SUPPORTED;
}

unsigned int MXC_UART_GetFlags(mxc_uart_regs_t *uart)
{
    return MXC_UART_RevB_GetFlags((mxc_uart_revb_regs_t *)uart);
}

int MXC_UART_ClearFlags(mxc_uart_regs_t *uart, unsigned int flags)
{
    return MXC_UART_RevB_ClearFlags((mxc_uart_revb_regs_t *)uart, flags);
}

int MXC_UART_EnableInt(mxc_uart_regs_t *uart, unsigned int mask)
{
    return MXC_UART_RevB_EnableInt((mxc_uart_revb_regs_t *)uart, mask);
}

int MXC_UART_DisableInt(mxc_uart_regs_t *uart, unsigned int mask)
{
    return MXC_UART_RevB_DisableInt((mxc_uart_revb_regs_t *)uart, mask);
}

unsigned int MXC_UART_GetStatus(mxc_uart_regs_t *uart)
{
    return MXC_UART_RevB_GetStatus((mxc_uart_revb_regs_t *)uart);
}

int MXC_UART_Transaction(mxc_uart_req_t *req)
{
    return MXC_UART_RevB_Transaction((mxc_uart_revb_req_t *)req);
}

int MXC_UART_TransactionAsync(mxc_uart_req_t *req)
{
    return MXC_UART_RevB_TransactionAsync((mxc_uart_revb_req_t *)req);
}

int MXC_UART_TransactionDMA(mxc_uart_req_t *req)
{
    return MXC_UART_RevB_TransactionDMA((mxc_uart_revb_req_t *)req);
}

int MXC_UART_AbortAsync(mxc_uart_regs_t *uart)
{
    return MXC_UART_RevB_AbortAsync((mxc_uart_revb_regs_t *)uart);
}

int MXC_UART_AsyncHandler(mxc_uart_regs_t *uart)
{
    return MXC_UART_RevB_AsyncHandler((mxc_uart_revb_regs_t *)uart);
}

uint32_t MXC_UART_GetAsyncTXCount(mxc_uart_req_t *req)
{
    return req->txCnt;
}

uint32_t MXC_UART_GetAsyncRXCount(mxc_uart_req_t *req)
{
    return req->rxCnt;
}
